package handlers

import (
	"database/sql"
	"encoding/json"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"

	"agentdesign/models"
)

// ListCanvasMessages returns persisted canvas conversation history for the page's active version.
func (h *Handler) ListCanvasMessages(c *gin.Context) {
	projectID := c.Params.ByName("id")
	pageID := c.Params.ByName("pageId")

	result := Result[[]models.CanvasMessage]{
		Code: http.StatusOK,
		Data: []models.CanvasMessage{},
		Msg:  ResponseMessage.SUCCESS,
	}

	page, err := h.service.GetProjectPage(projectID, pageID)
	if err != nil {
		if err == sql.ErrNoRows {
			result.Code = http.StatusNotFound
			result.Msg = "page not found"
		} else {
			result.Code = http.StatusInternalServerError
			result.Msg = err.Error()
		}
		c.JSON(result.Code, result)

		return
	}

	if page.CurrentVersionID == "" {
		c.JSON(result.Code, result)

		return
	}

	messages, err := h.service.ListCanvasMessages(page.CurrentVersionID)
	if err != nil {
		result.Code = http.StatusInternalServerError
		result.Msg = err.Error()
		c.JSON(result.Code, result)

		return
	}

	result.Data = messages

	c.JSON(result.Code, result)
}

// CreateCanvasMessageRequest describes the payload for posting a new canvas message.
type CreateCanvasMessageRequest struct {
	Author          string `json:"author" binding:"required"`
	Content         string `json:"content" binding:"required"`
	VersionID       string `json:"versionId"`
	PromptRequestID string `json:"promptRequestId"`
}

// CreateCanvasMessage persists a new canvas message for the target page version.
func (h *Handler) CreateCanvasMessage(c *gin.Context) {
	projectID := c.Params.ByName("id")
	pageID := c.Params.ByName("pageId")
	var req CreateCanvasMessageRequest

	result := Result[int64]{
		Code: http.StatusCreated,
		Data: 0,
		Msg:  ResponseMessage.SUCCESS,
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		result.Code = http.StatusBadRequest
		result.Msg = err.Error()
		c.JSON(result.Code, result)

		return
	}

	page, err := h.service.GetProjectPage(projectID, pageID)
	if err != nil {
		if err == sql.ErrNoRows {
			result.Code = http.StatusNotFound
			result.Msg = "page not found"
		} else {
			result.Code = http.StatusInternalServerError
			result.Msg = err.Error()
		}
		c.JSON(result.Code, result)

		return
	}

	versionID := strings.TrimSpace(req.VersionID)
	if versionID == "" {
		versionID = page.CurrentVersionID
	}

	if versionID == "" {
		result.Code = http.StatusBadRequest
		result.Msg = "no version is associated with this page"
		c.JSON(result.Code, result)

		return
	}

	var promptRequestID *string
	if trimmed := strings.TrimSpace(req.PromptRequestID); trimmed != "" {
		promptRequestID = &trimmed
	}

	id, err := h.service.CreateCanvasMessage(models.CanvasMessage{
		VersionID:       versionID,
		PromptRequestID: promptRequestID,
		Author:          req.Author,
		Content:         req.Content,
	})
	if err != nil {
		result.Code = http.StatusInternalServerError
		result.Msg = err.Error()
		c.JSON(result.Code, result)

		return
	}

	result.Data = id

	c.JSON(result.Code, result)
}

// ListPageSections returns structured sections for a page version.
func (h *Handler) ListPageSections(c *gin.Context) {
	projectID := c.Params.ByName("id")
	pageID := c.Params.ByName("pageId")
	versionID := strings.TrimSpace(c.Query("versionId"))

	result := Result[[]models.PageSection]{
		Code: http.StatusOK,
		Data: []models.PageSection{},
		Msg:  ResponseMessage.SUCCESS,
	}

	page, err := h.service.GetProjectPage(projectID, pageID)
	if err != nil {
		if err == sql.ErrNoRows {
			result.Code = http.StatusNotFound
			result.Msg = "page not found"
		} else {
			result.Code = http.StatusInternalServerError
			result.Msg = err.Error()
		}
		c.JSON(result.Code, result)

		return
	}

	if versionID == "" {
		versionID = page.CurrentVersionID
	}

	if versionID == "" {
		c.JSON(result.Code, result)

		return
	}

	sections, err := h.service.ListPageSections(versionID)
	if err != nil {
		result.Code = http.StatusInternalServerError
		result.Msg = err.Error()
		c.JSON(result.Code, result)

		return
	}

	result.Data = sections

	c.JSON(result.Code, result)
}

// ListLatestSection returns the most recent code snippet stored for the page version.
func (h *Handler) ListLatestSection(c *gin.Context) {
	projectID := c.Params.ByName("id")
	pageID := c.Params.ByName("pageId")
	versionID := strings.TrimSpace(c.Query("versionId"))

	result := Result[models.PageSection]{
		Code: http.StatusOK,
		Data: models.PageSection{},
		Msg:  ResponseMessage.SUCCESS,
	}

	page, err := h.service.GetProjectPage(projectID, pageID)
	if err != nil {
		if err == sql.ErrNoRows {
			result.Code = http.StatusNotFound
			result.Msg = "page not found"
		} else {
			result.Code = http.StatusInternalServerError
			result.Msg = err.Error()
		}
		c.JSON(result.Code, result)

		return
	}

	if versionID == "" {
		versionID = page.CurrentVersionID
	}

	if versionID == "" {
		result.Code = http.StatusBadRequest
		result.Msg = "no version is associated with this page"
		c.JSON(result.Code, result)

		return
	}

	section, err := h.service.GetLatestCodeSection(versionID)
	if err != nil {
		if err == sql.ErrNoRows {
			result.Data = models.PageSection{}
		} else {
			result.Code = http.StatusInternalServerError
			result.Msg = err.Error()
		}
		c.JSON(result.Code, result)

		return
	}

	result.Data = section

	c.JSON(result.Code, result)
}

// CreatePageSectionRequest describes the payload for a new section.
type CreatePageSectionRequest struct {
	VersionID     string          `json:"versionId"`
	SectionKey    string          `json:"sectionKey" binding:"required"`
	Label         string          `json:"label" binding:"required"`
	Description   string          `json:"description"`
	Position      int             `json:"position" binding:"required"`
	ComponentJSON json.RawMessage `json:"componentJson"`
}

// CreatePageSection adds a new section for the specified page version.
func (h *Handler) CreatePageSection(c *gin.Context) {
	projectID := c.Params.ByName("id")
	pageID := c.Params.ByName("pageId")
	var req CreatePageSectionRequest

	result := Result[models.PageSection]{
		Code: http.StatusCreated,
		Data: models.PageSection{},
		Msg:  ResponseMessage.SUCCESS,
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		result.Code = http.StatusBadRequest
		result.Msg = err.Error()
		c.JSON(result.Code, result)

		return
	}

	page, err := h.service.GetProjectPage(projectID, pageID)
	if err != nil {
		if err == sql.ErrNoRows {
			result.Code = http.StatusNotFound
			result.Msg = "page not found"
		} else {
			result.Code = http.StatusInternalServerError
			result.Msg = err.Error()
		}
		c.JSON(result.Code, result)

		return
	}

	versionID := strings.TrimSpace(req.VersionID)
	if versionID == "" {
		versionID = page.CurrentVersionID
	}

	if versionID == "" {
		result.Code = http.StatusBadRequest
		result.Msg = "no version is associated with this page"
		c.JSON(result.Code, result)

		return
	}

	section, err := h.service.CreatePageSection(models.PageSection{
		VersionID:     versionID,
		SectionKey:    req.SectionKey,
		Label:         req.Label,
		Description:   req.Description,
		Position:      req.Position,
		ComponentJSON: req.ComponentJSON,
	})
	if err != nil {
		result.Code = http.StatusInternalServerError
		result.Msg = err.Error()
		c.JSON(result.Code, result)

		return
	}

	result.Data = section

	c.JSON(result.Code, result)
}

// UpdatePageSectionRequest captures editable fields for a section.
type UpdatePageSectionRequest struct {
	VersionID     string          `json:"versionId"`
	SectionKey    string          `json:"sectionKey" binding:"required"`
	Label         string          `json:"label" binding:"required"`
	Description   string          `json:"description"`
	Position      int             `json:"position" binding:"required"`
	ComponentJSON json.RawMessage `json:"componentJson"`
}

// UpdatePageSection mutates an existing page section.
func (h *Handler) UpdatePageSection(c *gin.Context) {
	projectID := c.Params.ByName("id")
	pageID := c.Params.ByName("pageId")
	sectionID := c.Params.ByName("sectionId")
	var req UpdatePageSectionRequest

	result := Result[models.PageSection]{
		Code: http.StatusOK,
		Data: models.PageSection{},
		Msg:  ResponseMessage.SUCCESS,
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		result.Code = http.StatusBadRequest
		result.Msg = err.Error()
		c.JSON(result.Code, result)

		return
	}

	_, err := h.service.GetProjectPage(projectID, pageID)
	if err != nil {
		if err == sql.ErrNoRows {
			result.Code = http.StatusNotFound
			result.Msg = "page not found"
		} else {
			result.Code = http.StatusInternalServerError
			result.Msg = err.Error()
		}
		c.JSON(result.Code, result)

		return
	}

	currentSection, err := h.service.GetPageSection(sectionID)
	if err != nil {
		if err == sql.ErrNoRows {
			result.Code = http.StatusNotFound
			result.Msg = "section not found"
		} else {
			result.Code = http.StatusInternalServerError
			result.Msg = err.Error()
		}
		c.JSON(result.Code, result)

		return
	}

	versionID := strings.TrimSpace(req.VersionID)
	if versionID == "" {
		versionID = currentSection.VersionID
	}

	if versionID == "" {
		result.Code = http.StatusBadRequest
		result.Msg = "no version is associated with this page"
		c.JSON(result.Code, result)

		return
	}

	updatedSection, err := h.service.UpdatePageSection(models.PageSection{
		ID:            sectionID,
		VersionID:     versionID,
		SectionKey:    req.SectionKey,
		Label:         req.Label,
		Description:   req.Description,
		Position:      req.Position,
		ComponentJSON: req.ComponentJSON,
	})
	if err != nil {
		if err == sql.ErrNoRows {
			result.Code = http.StatusNotFound
			result.Msg = "section not found"
		} else {
			result.Code = http.StatusInternalServerError
			result.Msg = err.Error()
		}
		c.JSON(result.Code, result)

		return
	}

	result.Data = updatedSection

	c.JSON(result.Code, result)
}

// DeletePageSection removes a section definition from the page version.
func (h *Handler) DeletePageSection(c *gin.Context) {
	projectID := c.Params.ByName("id")
	pageID := c.Params.ByName("pageId")
	sectionID := c.Params.ByName("sectionId")

	result := Result[bool]{
		Code: http.StatusOK,
		Data: false,
		Msg:  ResponseMessage.SUCCESS,
	}

	_, err := h.service.GetProjectPage(projectID, pageID)
	if err != nil {
		if err == sql.ErrNoRows {
			result.Code = http.StatusNotFound
			result.Msg = "page not found"
		} else {
			result.Code = http.StatusInternalServerError
			result.Msg = err.Error()
		}
		c.JSON(result.Code, result)

		return
	}

	rows, err := h.service.DeletePageSection(sectionID)
	if err != nil {
		result.Code = http.StatusInternalServerError
		result.Msg = err.Error()
		c.JSON(result.Code, result)

		return
	}

	if rows == 0 {
		result.Code = http.StatusNotFound
		result.Msg = "section not found"
		c.JSON(result.Code, result)

		return
	}

	result.Data = true

	c.JSON(result.Code, result)
}

// ListPromptRequests returns placeholder prompt request entries.
func (h *Handler) ListPromptRequests(c *gin.Context) {
	placeholder := []gin.H{
		{
			"id":        "req-001",
			"projectId": "atlas",
			"pageId":    "atlas-primary-workspace",
			"status":    "completed",
			"createdAt": "2025-10-28T09:06:00Z",
		},
	}
	c.JSON(http.StatusOK, gin.H{"promptRequests": placeholder})
}
